{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第20章 潜在狄利克雷分配"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1.狄利克雷分布的概率密度函数为$$p ( \\theta | \\alpha ) = \\frac { \\Gamma ( \\sum _ { i = 1 } ^ { k } \\alpha _ { i } ) } { \\prod _ { i = 1 } ^ { k } \\Gamma ( \\alpha _ { i } ) } \\prod _ { i = 1 } ^ { k } \\theta _ { i } ^ { \\alpha _ { i } - 1 }$$\n",
    "其中$\\sum _ { i = 1 } ^ { k } \\theta _ { i } = 1 , \\theta _ { i } \\geq 0 , \\alpha = ( \\alpha _ { 1 } , \\alpha _ { 2 } , \\cdots , \\alpha _ { k } ) , \\alpha _ { i } > 0 , i = 1,2 , \\cdots , $狄利克雷分布是多项分布的共轭先验。\n",
    "\n",
    "2.潜在狄利克雷分配2.潜在狄利克雷分配（LDA）是文本集合的生成概率模型。模型假设话题由单词的多项分布表示，文本由话题的多项分布表示，单词分布和话题分布的先验分布都是狄利克雷分布。LDA模型属于概率图模型可以由板块表示法表示LDA模型中，每个话题的单词分布、每个文本的话题分布、文本的每个位置的话题是隐变量，文本的每个位置的单词是观测变量。\n",
    "\n",
    "3.LDA生成文本集合的生成过程如下：\n",
    "\n",
    "（1）话题的单词分布：随机生成所有话题的单词分布，话题的单词分布是多项分布，其先验分布是狄利克雷分布。\n",
    "\n",
    "（2）文本的话题分布：随机生成所有文本的话题分布，文本的话题分布是多项分布，其先验分布是狄利克雷分布。\n",
    "\n",
    "（3）文本的内容：随机生成所有文本的内容。在每个文本的每个位置，按照文本的话题分布随机生成一个话题，再按照该话题的单词分布随机生成一个单词。\n",
    "\n",
    "4.LDA模型的学习与推理不能直接求解。通常采用的方法是吉布斯抽样算法和变分EM算法，前者是蒙特卡罗法而后者是近似算法。\n",
    "\n",
    "5.LDA的收缩的吉布斯抽样算法的基本想法如下。目标是对联合概率分布$p ( w , z , \\theta , \\varphi | \\alpha , \\beta )$进行估计。通过积分求和将隐变量$\\theta$和$\\varphi$消掉，得到边缘概率分布$p ( w , z | \\alpha , \\beta )$；对概率分布$p ( w | z , \\alpha , \\beta )$进行吉布斯抽样，得到分布$p ( w | z , \\alpha , \\beta )$的随机样本；再利用样本对变量$z$，$\\theta$和$\\varphi$的概率进行估计，最终得到LDA模型$p ( w , z , \\theta , \\varphi | \\alpha , \\beta )$的参数估计。具体算法如下对给定的文本单词序列，每个位置上随机指派一个话题，整体构成话题系列。然后循环执行以下操作。对整个文本序列进行扫描，在每一个位置上计算在该位置上的话题的满条件概率分布，然后进行随机抽样，得到该位置的新的话题，指派给这个位置。\n",
    "\n",
    "6.变分推理的基本想法如下。假设模型是联合概率分布$p ( x , z )$，其中$x$是观测变量（数据），$z$是隐变量。目标是学习模型的后验概率分布$p ( z | x )$。考虑用变分分布$q ( z )$近似条件概率分布$p ( z | x )$，用KL散度计算两者的相似性找到与$p ( z | x )$在KL散度意义下最近的$q ^ { * } ( z )$，用这个分布近似$p ( z | x )$。假设$q ( z )$中的$z$的所有分量都是互相独立的。利用Jensen不等式，得到KL散度的最小化可以通过证据下界的最大化实现。因此，变分推理变成求解以下证据下界最大化问题：\n",
    "$$L ( q , \\theta ) = E _ { q } [ \\operatorname { log } p ( x , z | \\theta ) ] - E _ { q } [ \\operatorname { log } q ( z ) ]$$\n",
    "\n",
    "7.LDA的变分EM算法如下。针对LDA模型定义变分分布，应用变分EM算法。目标是对证据下界$L ( \\gamma , \\eta , \\alpha , \\varphi )$进行最大化，其中$\\alpha$和$\\varphi$是模型参数，$\\gamma$和$\\eta$是变分参数。交替迭代E步和M步，直到收敛。\n",
    "\n",
    "- （1）E步：固定模型参数$\\alpha$，$\\varphi$，通过关于变分参数$\\gamma$，$\\eta$的证据下界的最大化，估计变分参数$\\gamma$，$\\eta$。\n",
    "- （2）M步：固定变分参数$\\gamma$，$\\eta$，通过关于模型参数$\\alpha$，$\\varphi$的证据下界的最大化，估计模型参数$\\alpha$，$\\varphi$。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "潜在狄利克雷分配（latent Dirichlet allocation,LDA），作为基于贝叶斯学习的话题模型，是潜在语义分析、概率潜在语义分析的扩展，于2002年由Blei等提出dA在文本数据挖掘、图像处理、生物信息处理等领域被广泛使用。\n",
    "\n",
    "LDA模型是文本集合的生成概率模型假设每个文本由话题的一个多项分布表示，每个话题由单词的一个多项分布表示，特别假设文本的话题分布的先验分布是狄利克雷分布，话题的单词分布的先验分布也是狄利克雷分布。先验分布的导入使LDA能够更好地应对话题模型学习中的过拟合现象。\n",
    "\n",
    "LDA的文本集合的生成过程如下：首先随机生成一个文本的话题分布，之后在该文本的每个位置，依据该文本的话题分布随机生成一个话题，然后在该位置依据该话题的单词分布随机生成一个单词，直至文本的最后一个位置，生成整个文本。重复以上过程生成所有文本。\n",
    "\n",
    "LDA模型是含有隐变量的概率图模型。模型中，每个话题的单词分布，每个文本的话题分布，文本的每个位置的话题是隐变量；文本的每个位置的单词是观测变量。LDA模型的学习与推理无法直接求解通常使用吉布斯抽样（ Gibbs sampling）和变分EM算法（variational EM algorithm），前者是蒙特卡罗法，而后者是近似算法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "from gensim import corpora, models, similarities\n",
    "from pprint import pprint\n",
    "import warnings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "After\n"
     ]
    }
   ],
   "source": [
    "f = open('data/LDA_test.txt')\n",
    "stop_list = set('for a of the and to in'.split())\n",
    "# texts = [line.strip().split() for line in f]\n",
    "# print 'Before'\n",
    "# pprint(texts)\n",
    "print('After')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Text = \n",
      "[['human', 'machine', 'interface', 'lab', 'abc', 'computer', 'applications'],\n",
      " ['survey', 'user', 'opinion', 'computer', 'system', 'response', 'time'],\n",
      " ['eps', 'user', 'interface', 'management', 'system'],\n",
      " ['system', 'human', 'system', 'engineering', 'testing', 'eps'],\n",
      " ['relation', 'user', 'perceived', 'response', 'time', 'error', 'measurement'],\n",
      " ['generation', 'random', 'binary', 'unordered', 'trees'],\n",
      " ['intersection', 'graph', 'paths', 'trees'],\n",
      " ['graph', 'minors', 'iv', 'widths', 'trees', 'well', 'quasi', 'ordering'],\n",
      " ['graph', 'minors', 'survey']]\n"
     ]
    }
   ],
   "source": [
    "texts = [[\n",
    "    word for word in line.strip().lower().split() if word not in stop_list\n",
    "] for line in f]\n",
    "print('Text = ')\n",
    "pprint(texts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dictionary(35 unique tokens: ['abc', 'applications', 'computer', 'human', 'interface']...)\n"
     ]
    }
   ],
   "source": [
    "dictionary = corpora.Dictionary(texts)\n",
    "print(dictionary)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TF-IDF:\n",
      "[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1)]\n",
      "[(2, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), (12, 1)]\n",
      "[(4, 1), (10, 1), (12, 1), (13, 1), (14, 1)]\n",
      "[(3, 1), (10, 2), (13, 1), (15, 1), (16, 1)]\n",
      "[(8, 1), (11, 1), (12, 1), (17, 1), (18, 1), (19, 1), (20, 1)]\n",
      "[(21, 1), (22, 1), (23, 1), (24, 1), (25, 1)]\n",
      "[(24, 1), (26, 1), (27, 1), (28, 1)]\n",
      "[(24, 1), (26, 1), (29, 1), (30, 1), (31, 1), (32, 1), (33, 1), (34, 1)]\n",
      "[(9, 1), (26, 1), (30, 1)]\n"
     ]
    }
   ],
   "source": [
    "V = len(dictionary)\n",
    "corpus = [dictionary.doc2bow(text) for text in texts]\n",
    "corpus_tfidf = models.TfidfModel(corpus)[corpus]\n",
    "corpus_tfidf = corpus\n",
    "\n",
    "print('TF-IDF:')\n",
    "for c in corpus_tfidf:\n",
    "    print(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "LSI Model:\n",
      "[[(0, 0.9334981916792645), (1, -0.10508952614086088)],\n",
      " [(0, 2.0319923746870256), (1, 0.047145314121738426)],\n",
      " [(0, 1.535134283658208), (1, -0.13488784052204592)],\n",
      " [(0, 1.9540077194594518), (1, -0.21780498576074706)],\n",
      " [(0, 1.290247295600412), (1, 0.0022521437499299773)],\n",
      " [(0, 0.022783081905506503), (1, 0.7778052604326777)],\n",
      " [(0, 0.05671567576920933), (1, 1.1827703446704863)],\n",
      " [(0, 0.12360003320647997), (1, 2.6343068608236835)],\n",
      " [(0, 0.23560627195889128), (1, 0.940793620366831)]]\n"
     ]
    }
   ],
   "source": [
    "print('\\nLSI Model:')\n",
    "lsi = models.LsiModel(corpus_tfidf, num_topics=2, id2word=dictionary)\n",
    "topic_result = [a for a in lsi[corpus_tfidf]]\n",
    "pprint(topic_result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LSI Topics:\n",
      "[(0,\n",
      "  '0.579*\"system\" + 0.376*\"user\" + 0.270*\"eps\" + 0.257*\"time\" + '\n",
      "  '0.257*\"response\"'),\n",
      " (1,\n",
      "  '0.480*\"graph\" + 0.464*\"trees\" + 0.361*\"minors\" + 0.266*\"widths\" + '\n",
      "  '0.266*\"iv\"')]\n"
     ]
    }
   ],
   "source": [
    "print('LSI Topics:')\n",
    "pprint(lsi.print_topics(num_topics=2, num_words=5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Similarity:\n",
      "[array([ 1.        ,  0.9908607 ,  0.9997008 ,  0.9999994 ,  0.9935261 ,\n",
      "       -0.08272626, -0.06414512, -0.06517283,  0.13288835], dtype=float32),\n",
      " array([0.9908607 , 0.99999994, 0.9938636 , 0.99100804, 0.99976987,\n",
      "       0.0524564 , 0.07105229, 0.070025  , 0.2653665 ], dtype=float32),\n",
      " array([ 0.9997008 ,  0.9938636 ,  0.99999994,  0.999727  ,  0.99600756,\n",
      "       -0.05832579, -0.03971674, -0.04074576,  0.15709123], dtype=float32),\n",
      " array([ 0.9999994 ,  0.99100804,  0.999727  ,  1.        ,  0.9936501 ,\n",
      "       -0.08163348, -0.06305084, -0.06407862,  0.13397504], dtype=float32),\n",
      " array([0.9935261 , 0.99976987, 0.99600756, 0.9936501 , 0.99999994,\n",
      "       0.03102366, 0.04963995, 0.04861134, 0.24462426], dtype=float32),\n",
      " array([-0.08272626,  0.0524564 , -0.05832579, -0.08163348,  0.03102366,\n",
      "        0.99999994,  0.99982643,  0.9998451 ,  0.97674036], dtype=float32),\n",
      " array([-0.06414512,  0.07105229, -0.03971674, -0.06305084,  0.04963995,\n",
      "        0.99982643,  1.        ,  0.9999995 ,  0.9805657 ], dtype=float32),\n",
      " array([-0.06517283,  0.070025  , -0.04074576, -0.06407862,  0.04861134,\n",
      "        0.9998451 ,  0.9999995 ,  1.        ,  0.9803632 ], dtype=float32),\n",
      " array([0.13288835, 0.2653665 , 0.15709123, 0.13397504, 0.24462426,\n",
      "       0.97674036, 0.9805657 , 0.9803632 , 1.        ], dtype=float32)]\n"
     ]
    }
   ],
   "source": [
    "similarity = similarities.MatrixSimilarity(lsi[corpus_tfidf])   # similarities.Similarity()\n",
    "print('Similarity:')\n",
    "pprint(list(similarity))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "LDA Model:\n",
      "Document-Topic:\n",
      "\n",
      "[[(0, 0.9822428), (1, 0.017757222)],\n",
      " [(0, 0.98223716), (1, 0.017762838)],\n",
      " [(0, 0.9755337), (1, 0.024466258)],\n",
      " [(0, 0.97942173), (1, 0.020578273)],\n",
      " [(0, 0.98224264), (1, 0.017757382)],\n",
      " [(0, 0.030637555), (1, 0.9693625)],\n",
      " [(0, 0.037769396), (1, 0.96223056)],\n",
      " [(0, 0.019549757), (1, 0.9804503)],\n",
      " [(0, 0.049358923), (1, 0.9506411)]]\n"
     ]
    }
   ],
   "source": [
    "print('\\nLDA Model:')\n",
    "num_topics = 2\n",
    "lda = models.LdaModel(\n",
    "    corpus_tfidf,\n",
    "    num_topics=num_topics,\n",
    "    id2word=dictionary,\n",
    "    alpha='auto',\n",
    "    eta='auto',\n",
    "    minimum_probability=0.001,\n",
    "    passes=10)\n",
    "doc_topic = [doc_t for doc_t in lda[corpus_tfidf]]\n",
    "print('Document-Topic:\\n')\n",
    "pprint(doc_topic)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(0, 0.98224276), (1, 0.017757224)]\n",
      "[(0, 0.9822373), (1, 0.017762741)]\n",
      "[(0, 0.9755337), (1, 0.024466285)]\n",
      "[(0, 0.9794219), (1, 0.020578118)]\n",
      "[(0, 0.9822425), (1, 0.017757487)]\n",
      "[(0, 0.030637614), (1, 0.9693624)]\n",
      "[(0, 0.037769392), (1, 0.96223056)]\n",
      "[(0, 0.019549662), (1, 0.9804504)]\n",
      "[(0, 0.049359232), (1, 0.9506408)]\n"
     ]
    }
   ],
   "source": [
    "for doc_topic in lda.get_document_topics(corpus_tfidf):\n",
    "    print(doc_topic)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Topic 0\n",
      "[('system', 0.09066802),\n",
      " ('user', 0.07064208),\n",
      " ('computer', 0.050511736),\n",
      " ('interface', 0.050501335),\n",
      " ('time', 0.050459914),\n",
      " ('response', 0.05042733),\n",
      " ('human', 0.05038273),\n",
      " ('eps', 0.050382525),\n",
      " ('survey', 0.030469572),\n",
      " ('opinion', 0.030311268)]\n",
      "Topic 1\n",
      "[('graph', 0.09305157),\n",
      " ('trees', 0.09303341),\n",
      " ('minors', 0.066461794),\n",
      " ('quasi', 0.03990646),\n",
      " ('iv', 0.039903373),\n",
      " ('well', 0.0399015),\n",
      " ('ordering', 0.039900467),\n",
      " ('widths', 0.039900415),\n",
      " ('intersection', 0.039853666),\n",
      " ('generation', 0.039846685)]\n",
      "Similarity:\n",
      "[array([1.        , 0.99999994, 0.9999755 , 0.9999957 , 0.99999994,\n",
      "       0.04965142, 0.05727691, 0.03800426, 0.06989466], dtype=float32),\n",
      " array([0.99999994, 0.99999994, 0.99997556, 0.9999957 , 0.99999994,\n",
      "       0.04965696, 0.05728246, 0.03800981, 0.06990019], dtype=float32),\n",
      " array([0.9999755 , 0.99997556, 1.        , 0.9999917 , 0.9999755 ,\n",
      "       0.05663961, 0.06426205, 0.04499632, 0.07687387], dtype=float32),\n",
      " array([0.9999957 , 0.9999957 , 0.9999917 , 1.        , 0.9999957 ,\n",
      "       0.05257849, 0.06020276, 0.04093289, 0.07281809], dtype=float32),\n",
      " array([0.99999994, 0.99999994, 0.9999755 , 0.9999957 , 0.99999994,\n",
      "       0.04965121, 0.0572767 , 0.03800405, 0.06989444], dtype=float32),\n",
      " array([0.04965142, 0.04965696, 0.05663961, 0.05257849, 0.04965121,\n",
      "       1.        , 0.99997085, 0.99993205, 0.99979436], dtype=float32),\n",
      " array([0.05727691, 0.05728246, 0.06426205, 0.06020276, 0.0572767 ,\n",
      "       0.99997085, 0.99999994, 0.99981385, 0.99992007], dtype=float32),\n",
      " array([0.03800426, 0.03800981, 0.04499632, 0.04093289, 0.03800405,\n",
      "       0.99993205, 0.99981385, 1.        , 0.99949   ], dtype=float32),\n",
      " array([0.06989466, 0.06990019, 0.07687387, 0.07281809, 0.06989444,\n",
      "       0.99979436, 0.99992007, 0.99949   , 1.        ], dtype=float32)]\n",
      "\n",
      "\n",
      "USE WITH CARE--\n",
      "HDA Model:\n",
      "[[(0, 0.1950458757990083),\n",
      "  (1, 0.7342816536987412),\n",
      "  (2, 0.017840867996792364),\n",
      "  (3, 0.013304728623403938),\n",
      "  (4, 0.010091990784269944)],\n",
      " [(0, 0.1754011751462379),\n",
      "  (1, 0.7539044806000282),\n",
      "  (2, 0.01786539509159517),\n",
      "  (3, 0.013302318943979213),\n",
      "  (4, 0.010091752823503377)],\n",
      " [(0, 0.04540573611699031),\n",
      "  (1, 0.8602752271595292),\n",
      "  (2, 0.023867683910288182),\n",
      "  (3, 0.017748432320143528),\n",
      "  (4, 0.013456416454219635)],\n",
      " [(0, 0.3014421271724018),\n",
      "  (1, 0.6176988300374067),\n",
      "  (2, 0.020481595233911118),\n",
      "  (3, 0.01520418001494075),\n",
      "  (4, 0.011533401985138124)],\n",
      " [(0, 0.9051275659026145),\n",
      "  (1, 0.024100081801173696),\n",
      "  (2, 0.017945950898097217),\n",
      "  (3, 0.013299658786651975),\n",
      "  (4, 0.010091860233903767)],\n",
      " [(0, 0.0471925404677171),\n",
      "  (1, 0.03353198642932736),\n",
      "  (2, 0.8488330065012414),\n",
      "  (3, 0.017739520450520394),\n",
      "  (4, 0.01345644394510441)],\n",
      " [(0, 0.05113401830861678),\n",
      "  (1, 0.03865947163112331),\n",
      "  (2, 0.8256830610769758),\n",
      "  (3, 0.02128110731386981),\n",
      "  (4, 0.016146540415629778),\n",
      "  (5, 0.011999526458454528)],\n",
      " [(0, 0.028996582268353277),\n",
      "  (1, 0.25763236083854174),\n",
      "  (2, 0.6664138028921944),\n",
      "  (3, 0.011822636214658224)],\n",
      " [(0, 0.068025617114071),\n",
      "  (1, 0.7905931693925448),\n",
      "  (2, 0.035725493019188266),\n",
      "  (3, 0.026602930265055707),\n",
      "  (4, 0.02018302949361456),\n",
      "  (5, 0.01499941717929438),\n",
      "  (6, 0.011331486934610353)]]\n",
      "HDA Topics:\n",
      "[(0, '0.086*widths + 0.083*relation + 0.075*random + 0.073*intersection + 0.069*time'), (1, '0.158*random + 0.102*widths + 0.075*time + 0.060*paths + 0.059*intersection')]\n"
     ]
    }
   ],
   "source": [
    "for topic_id in range(num_topics):\n",
    "    print('Topic', topic_id)\n",
    "    # pprint(lda.get_topic_terms(topicid=topic_id))\n",
    "    pprint(lda.show_topic(topic_id))\n",
    "similarity = similarities.MatrixSimilarity(lda[corpus_tfidf])\n",
    "print('Similarity:')\n",
    "pprint(list(similarity))\n",
    "\n",
    "hda = models.HdpModel(corpus_tfidf, id2word=dictionary)\n",
    "topic_result = [a for a in hda[corpus_tfidf]]\n",
    "print('\\n\\nUSE WITH CARE--\\nHDA Model:')\n",
    "pprint(topic_result)\n",
    "print('HDA Topics:')\n",
    "print(hda.print_topics(num_topics=2, num_words=5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "----\n",
    "代码参考：邹博\n",
    "\n",
    "中文注释制作：机器学习初学者公众号：ID:ai-start-com\n",
    "\n",
    "配置环境：python 3.5+\n",
    "\n",
    "代码全部测试通过。\n",
    "![gongzhong](../gongzhong.jpg)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
